package org.hard.netty.c2;

import lombok.extern.slf4j.Slf4j;
import org.hard.netty.ByteBufferUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * @author: 王林博
 * @date: 2022年08月13日 9:14
 */
@Slf4j
public class SelectorServer {
    private static void split(ByteBuffer source){
        source.flip(); // 读模式
        for (int i = 0; i < source.limit(); i++) {
            if (source.get(i) == '\n') { // 截取
                int length = i + 1 - source.position();
                ByteBuffer target = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    target.put(source.get());
                }
                target.flip();
//                System.out.println((char) target.get());
//                System.out.println((char) target.get(target.position()));
                ByteBufferUtil.debugAll(target);

            }
        }

        source.compact();// 写模式 ->最后未读的往前移动
    }

    public static void main(String[] args) throws IOException {

        Selector selector = Selector.open();

        //1.创建服务器
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        socketChannel.configureBlocking(false); // 默认true 阻塞模式 ，false 非阻塞模式
        // SelectionKey就是将来事件发生后，知道时哪个channel发生的
        SelectionKey register = socketChannel.register(selector, 0, null);
        // key 只关注 accept 事件
        register.interestOps(SelectionKey.OP_ACCEPT);
        log.debug("key :{}",register);
        // 2.绑定监听端口
        socketChannel.bind(new InetSocketAddress(8080));


        // 3.accept
        while (true) {
            // 3. select 方法，没有事件发生，线程阻塞
            // 有事件未处理时，不会阻塞，必须需要处理，无论取消/处理
           selector.select();
           // 4.处理事件，SelectionKey 中包含了所有发生的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                // 处理key的时候需要将key的集合中删除，要不然下次还会进行处理（但是已经为空
                iterator.remove();
                log.debug("key :{}",key);
                if(key.isAcceptable()){
                    ServerSocketChannel channel = (ServerSocketChannel)key.channel();
                    SocketChannel accept = channel.accept();
                    accept.configureBlocking(false);
                    log.debug("accept:{}",accept);
                    ByteBuffer buffer = ByteBuffer.allocate(16);        // attachment 如果超过16字节，下一次用到的还是同一个
                    // 将buffer作为附件关联到SelectionKey 上
                    SelectionKey selectionKey = accept.register(selector, 0, buffer);
                    selectionKey.interestOps(SelectionKey.OP_READ);

                }else if(key.isReadable()){
                    try {
                        SocketChannel channel = (SocketChannel)key.channel();
                        ByteBuffer buffer = (ByteBuffer)key.attachment();
//                        ByteBuffer buffer = ByteBuffer.allocate(16);
                        // 如果客户端正常断开 则read返回的是 -1
                        final int read = channel.read(buffer);
                        if(read == -1){
                            key.cancel();
                        }else {

                            split(buffer);
                            if(buffer.position() == buffer.limit()){ // 表示占满了，需要更大的空间读取未读的信息
                                ByteBuffer newByteBuffer = ByteBuffer.allocate(buffer.capacity()*2);
                                buffer.flip(); // 切换成读模式
                                newByteBuffer.put(buffer);
                                key.attach(newByteBuffer); // 重新关联byteBuffer
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        key.cancel();// 因为客户端断开了，因此需要将key从selector的keys集合中删除
                    }
                }



            }

        }
    }
}
